home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / nevow / canvas.py < prev    next >
Text File  |  2006-04-14  |  15KB  |  475 lines

  1. # Copyright (c) 2004 Divmod.
  2. # See LICENSE for details.
  3.  
  4. from zope.interface import implements
  5.  
  6. from twisted.internet import defer
  7. from twisted.python import log
  8.  
  9. from nevow import inevow, rend, loaders, static, url, tags, util
  10. from nevow.flat import flatten
  11. from nevow.stan import Proto, Tag
  12. from itertools import count
  13.  
  14. cn = count().next
  15. cookie = lambda: str(cn())
  16.  
  17. _hookup = {}
  18.  
  19. ## If we need to use Canvas through a CGI which forwards to the appserver,
  20. ## then we will need to listen with the canvas protocol on another socket
  21. ## so the canvas movie can push data to us. Here is where we will keep it.
  22. _canvasCGIService = None
  23.  
  24. # m = method
  25. # a = argument
  26. # <m n="moveTo" t="canvas">
  27. #   <a v="16" />
  28. #   <a v="16" />
  29. # </m>
  30.  
  31.  
  32. m = Proto('m') # method call; contains arguments
  33. a = Proto('a') # argument; has v="" attribute for simple value, or <l> or <d> child for list or dict value
  34. l = Proto('l') # list; has <a> children; <a> children must be simple values currently
  35. d = Proto('d') # dict; has <i> children
  36. i = Proto('i') # dict item; has k="" for key and v="" for simple value (no nested dicts yet)
  37.  
  38.  
  39. def squish(it):
  40.     if isinstance(it, Tag):
  41.         return a[it]
  42.     return a(v=it)
  43.  
  44.  
  45. class _Remoted(object):
  46.     def __init__(self, cookie, canvas):
  47.         self.cookie = cookie
  48.         self.canvas = canvas
  49.  
  50.  
  51. class Text(_Remoted):
  52.     x = 0
  53.     y = 0
  54.     def change(self, text):
  55.         self.text = text
  56.         self.canvas.call('changeText', self.cookie, text)
  57.  
  58.     def move(self, x, y):
  59.         self.x = x
  60.         self.y = y
  61.         self.canvas.call('moveText', self.cookie, x, y)
  62.  
  63.     def listFonts(self):
  64.         if hasattr(self.canvas, '_fontList'):
  65.             return defer.succeed(self.canvas._fontList)
  66.         cook = cookie()
  67.         self.canvas.deferreds[cook] = d = defer.Deferred()
  68.         self.canvas.call('listFonts', cook)
  69.         def _cb(l):
  70.             L = l.split(',')
  71.             self.canvas._fontList = L
  72.             return L
  73.         return d.addCallback(_cb)
  74.  
  75.     def font(self, font):
  76.         self.canvas.call('font', self.cookie, font)
  77.  
  78.     def size(self, size):
  79.         self.canvas.call('size', self.cookie, size)
  80.  
  81.  
  82. class Image(_Remoted):
  83.     def move(self, x, y):
  84.         self.canvas.call('moveImage', self.cookie, x, y)
  85.  
  86.     def scale(self, x, y):
  87.         self.canvas.call('scaleImage', self.cookie, x, y)
  88.  
  89.     def alpha(self, alpha):
  90.         self.canvas.call('alphaImage', self.cookie, alpha)
  91.  
  92.     def rotate(self, angle):
  93.         self.canvas.call('rotateImage', self.cookie, angle)
  94.  
  95.  
  96. class Sound(_Remoted):
  97.     def play(self, offset=0, timesLoop=0):
  98.         """Play the sound, starting at "offset", in seconds. Loop the sound "timesLoop"
  99.         times.
  100.         """
  101.         self.canvas.call('playSound', self.cookie, offset, timesLoop)
  102.  
  103.  
  104. class GroupBase(object):
  105.     def call(self, method, *args):
  106.         """Call a client-side method with the given arguments. Arguments
  107.         will be converted to strings. You should probably use the other higher-level
  108.         apis instead.
  109.         """
  110.         flatcall = flatten(
  111.             m(n=method, t=self.groupName)[[
  112.                 squish(x) for x in args if x is not None]])
  113.         self.socket.write(flatcall + '\0')
  114.  
  115.     groupx = 0
  116.     groupy = 0
  117.     def reposition(self, x, y):
  118.         """Reposition all the elements in this group
  119.         """
  120.         self.groupx = x
  121.         self.groupy = y
  122.         self.call('reposition', x, y)
  123.  
  124.     def rotate(self, angle):
  125.         """Rotate all the elements of this group
  126.         """
  127.         self.call('rotate', angle)
  128.  
  129.     _alpha = 100
  130.     def alpha(self, percent):
  131.         """Set the alpha value of this group
  132.         """
  133.         self._alpha = percent
  134.         self.call('alpha', percent)
  135.  
  136.     def line(self, x, y):
  137.         """Draw a line from the current point to the given point.
  138.  
  139.         (0,0) is in the center of the canvas.
  140.         """
  141.         self.call('line', x, y)
  142.  
  143.     x = 0
  144.     y = 0
  145.     def move(self, x, y):
  146.         """Move the pen to the given point.
  147.     
  148.         (0, 0) is in the center of the canvas.
  149.         """
  150.         self.x = x
  151.         self.y = y
  152.         self.call('move', x, y)
  153.  
  154.     def pen(self, width=None, rgb=None, alpha=None):
  155.         """Change the current pen attributes. 
  156.  
  157.         width: an integer between 0 and 255; the pen thickness, in pixels.
  158.         rgb: an integer between 0x000000 and 0xffffff
  159.         alpha: an integer between 0 and 100; the opacity of the pen
  160.         """
  161.         self.call('pen', width, rgb, alpha)
  162.  
  163.     def clear(self):
  164.         """Clear the current pen attributes.
  165.         """
  166.         self.call('clear')
  167.  
  168.     def fill(self, rgb, alpha=100):
  169.         """Set the current fill. Fill will not be drawn until close is called.
  170.         
  171.         rgb: color of fill, integer between 0x000000 and 0xffffff
  172.         alpha: an integer between 0 and 100; the opacity of the fill
  173.         """
  174.         self.call('fill', rgb, alpha)
  175.  
  176.     def close(self):
  177.         """Close the current shape. A line will be drawn from the end point
  178.         to the start point, and the shape will be filled with the current fill.
  179.         """
  180.         self.call('close')
  181.  
  182.     def curve(self, controlX, controlY, anchorX, anchorY):
  183.         """Draw a curve
  184.         """
  185.         self.call('curve', controlX, controlY, anchorX, anchorY)
  186.  
  187.     def gradient(self, type, colors, alphas, ratios, matrix):
  188.         """Draw a gradient. Currently the API for this sucks, see the flash documentation
  189.         for info. Higher level objects for creating gradients will hopefully be developed
  190.         eventually.
  191.         """
  192.         self.call('gradient', type,
  193.             l[[a(v=x) for x in colors]],
  194.             l[[a(v=x) for x in alphas]],
  195.             l[[a(v=x) for x in ratios]],
  196.             d[[i(k=k, v=v) for (k, v) in matrix.items()]])
  197.  
  198.     def text(self, text, x, y, height, width):
  199.         """Place the given text on the canvas using the given x, y, height and width.
  200.         The result is a Text object which can be further manipulated to affect the text.
  201.         """
  202.         cook = cookie()
  203.         t = Text(cook, self)
  204.         t.text = text
  205.         self.call('text', cook, text, x, y, height, width)
  206.         return t
  207.  
  208.     def image(self, where):
  209.         """Load an image from the URL "where". The result is an Image object which
  210.         can be further manipulated to move it or change rotation.
  211.         """
  212.         cook = cookie()
  213.         I = Image(cook, self)
  214.         self.call('image', cook, where)
  215.         print "IMAGE", where
  216.         return I
  217.  
  218.     def sound(self, where, stream=True):
  219.         """Load an mp3 from the URL "where". The result is a Sound object which
  220.         can be further manipulated.
  221.  
  222.         If stream is True, the sound will play as soon as possible. If false, 
  223.         """
  224.         cook = cookie()
  225.         S = Sound(cook, self)
  226.         self.call('sound', cook, where, stream and 1 or 0)
  227.         return S
  228.  
  229.     def group(self):
  230.         """Create a new group of shapes. The returned object will
  231.         have all of the same APIs for drawing, except the grouped
  232.         items can all be moved simultaneously, deleted, etc.
  233.         """
  234.         cook = cookie()
  235.         G = Group('%s.G_%s' % (self.groupName, cook), self.socket, self)
  236.         self.call('group', cook)
  237.         return G
  238.  
  239.  
  240. class Group(GroupBase):
  241.     def __init__(self, groupName, socket, canvas):
  242.         self.groupName = groupName
  243.         self.socket = socket
  244.         self.canvas = canvas
  245.         self.deferreds = canvas.deferreds
  246.  
  247.     closed = property(lambda self: self.canvas.closed)
  248.  
  249.     def setMask(self, other=None):
  250.         """Set the mask of self to the group "other". "other" must be a Group
  251.         instance, if provided. If not provided, any previous mask will be removed
  252.         from self.
  253.         """
  254.         if other is None:
  255.             self.call('setMask', '')
  256.         else:
  257.             self.call('setMask', other.groupName)
  258.  
  259.     def setVisible(self, visible):
  260.         self.call('setVisible', str(bool(visible)))
  261.  
  262.     xscale = 100
  263.     yscale = 100
  264.     def scale(self, x, y):
  265.         self.call('scale', x, y)
  266.  
  267.     def swapDepth(self, intOrGroup):
  268.         """Swap the z-order depth of this group with another.
  269.         If an int is provided, the group will be placed at that depth,
  270.         regardless of whether there is an existing clip there.
  271.         If a group is provided, the z depth of self and the other group
  272.         are swapped.
  273.         """
  274.         if isinstance(intOrGroup, Group):
  275.             self.call('swapGroup', intOrGroup.groupName)
  276.         else:
  277.             self.call('swapInt', intOrGroup)
  278.  
  279.     def depth(self):
  280.         """Return a deferred which will fire the depth of this group.
  281.         XXX TODO
  282.         """
  283.         return 0
  284.  
  285.  
  286. class CanvasSocket(GroupBase):
  287.     """An object which represents the client-side canvas. Defines APIs for drawing
  288.     on the canvas. An instance of this class will be passed to your onload callback.
  289.     """
  290.     implements(inevow.IResource)
  291.  
  292.     groupName = 'canvas'
  293.  
  294.     closed = False
  295.     def __init__(self):
  296.         self.canvas = self
  297.         self.d = defer.Deferred().addErrback(log.err)
  298.  
  299.     def locateChild(self, ctx, segs):
  300.         self.cookie = segs[0]
  301.         return (self, ())
  302.  
  303.     def renderHTTP(self, ctx):
  304.         try:
  305.             self.deferreds = {}
  306.             self.buffer = ''
  307.             ## Don't try this at home kids! You'll blow your arm off!
  308.             self.socket = inevow.IRequest(ctx).transport
  309.             ## We be hijackin'
  310.             self.socket.protocol = self
  311.             ## This request never finishes until the user leaves the page
  312.             self.delegate = _hookup[self.cookie]
  313.             self.delegate.onload(self)
  314.             del _hookup[self.cookie]
  315.         except:
  316.             log.err()
  317.         return self.d
  318.  
  319.     def dataReceived(self, data):
  320.         self.buffer += data
  321.         while '\0' in self.buffer:
  322.             I = self.buffer.index('\0')
  323.             message = self.buffer[:I]
  324.             self.buffer = self.buffer[I+1:]
  325.             self.gotMessage(message)
  326.  
  327.     def gotMessage(self, message):
  328.         I = message.index(' ')
  329.         handler = getattr(self, 'handle_%s' % (message[:I], ), None)
  330.         if handler is not None:
  331.             handler(message[I+1:])
  332.         else:
  333.             self.deferreds[message[:I]].callback(message[I+1:])
  334.             del self.deferreds[message[:I]]
  335.  
  336.     def connectionLost(self, reason):
  337.         self.closed = True
  338.         del self.socket
  339.  
  340.     def done(self):
  341.         """Done drawing; close the connection with the movie
  342.         """
  343.         ## All done with the request object
  344.         self.closed = True
  345.         self.d.callback('')
  346.  
  347.     def handle_onKeyDown(self, info):
  348.         if self.delegate.onKeyDown:
  349.             self.delegate.onKeyDown(self, chr(int(info)))
  350.  
  351.     def handle_onKeyUp(self, info):
  352.         if self.delegate.onKeyUp:
  353.             self.delegate.onKeyUp(self, chr(int(info)))
  354.  
  355.     def handle_onMouseUp(self, info):
  356.         if self.delegate.onMouseUp:
  357.             self.delegate.onMouseUp(self, *map(int, map(float, info.split())))
  358.  
  359.     def handle_onMouseDown(self, info):
  360.         if self.delegate.onMouseDown:
  361.             self.delegate.onMouseDown(self, *map(int, map(float, info.split())))
  362.  
  363.     def handle_onMouseMove(self, info):
  364.         if self.delegate.onMouseMove:
  365.             self.delegate.onMouseMove(self, *map(int, map(float, info.split())))
  366.  
  367.     def handle_diagnostic(self, info):
  368.         print "Trace", info
  369.  
  370. canvasServerMessage = loaders.stan(tags.html["This server dispatches for nevow canvas events."])
  371.  
  372.  
  373. def canvas(width, height, delegate, useCGI=False):
  374.     C = cookie()
  375.     if useCGI:
  376.         global _canvasCGIService
  377.         if _canvasCGIService is None:
  378.             from nevow import appserver
  379.             # Import reactor here to avoid installing default at startup
  380.             from twisted.internet import reactor
  381.             _canvasCGIService = reactor.listenTCP(0, appserver.NevowSite(Canvas(docFactory=canvasServerMessage)))
  382.             _canvasCGIService.dispatchMap = {}
  383.         port = _canvasCGIService.getHost().port
  384.         prefix = '/'
  385.         movie_url = url.here.click('/').secure(False, port)
  386.     else:
  387.         movie_url = url.here
  388.         port = lambda c, d: inevow.IRequest(c).transport.server.port
  389.         def prefix(c, d):
  390.             pre = inevow.IRequest(c).path
  391.             if pre.endswith('/'):
  392.                 return pre
  393.             return pre + '/'
  394.  
  395.     _hookup[C] = delegate
  396.     handlerInfo = []
  397.     for handlerName in ['onMouseMove', 'onMouseDown', 'onMouseUp', 'onKeyDown', 'onKeyUp']:
  398.         if getattr(delegate, handlerName, None) is not None:
  399.             handlerInfo.append((handlerName, 1))
  400.  
  401.     movie_url = movie_url.child('nevow_canvas_movie.swf').add('cookie', C).add('port', port).add('prefix', prefix)
  402.     for (k, v) in handlerInfo:
  403.         movie_url = movie_url.add(k, v)
  404.  
  405.     return tags._object(classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000",
  406.         codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0",
  407.         width=width, height=height, id=("Canvas-", C), align="middle")[
  408.         tags.param(name="allowScriptAccess", value="sameDomain"),
  409.         tags.param(name="movie", value=movie_url),
  410.         tags.param(name="quality", value="high"),
  411.         tags.param(name="scale", value="noscale"),
  412.         tags.param(name="bgcolor", value="#ffffff"),
  413.         Tag('embed')(
  414.             src=movie_url,
  415.             quality="high",
  416.             scale="noscale",
  417.             bgcolor="#ffffff",
  418.             width=width,
  419.             height=height,
  420.             name=("Canvas-", C),
  421.             align="middle",
  422.             allowScriptAccess="sameDomain",
  423.             type="application/x-shockwave-flash",
  424.             pluginspage="http://www.macromedia.com/go/getflashplayer")]
  425.  
  426.  
  427. class Canvas(rend.Page):
  428.     """A page which can embed canvases. Simplest usage is to subclass and
  429.     override width, height and onload. Then, putting render_canvas in the
  430.     template will output that canvas there.
  431.     
  432.     You can also embed more than one canvas in a page using the canvas
  433.     helper function, canvas(width, height, onload). The resulting stan
  434.     will cause a canvas of the given height and width to be embedded in
  435.     the page at that location, and the given onload callable to be called
  436.     with a CanvasSocket when the connection is established.
  437.     """
  438.     addSlash = True
  439.     def __init__(self, original=None, width=None, height=None, onload=None, 
  440.     onMouseMove=None, onMouseDown=None, onMouseUp=None, 
  441.     onKeyDown=None, onKeyUp=None, **kw):
  442.         rend.Page.__init__(self, original, **kw)
  443.         if width: self.width = width
  444.         if height: self.height = height
  445.         if onload: self.onload = onload
  446.         if onMouseMove: self.onMouseMove = onMouseMove
  447.         if onMouseDown: self.onMouseDown = onMouseDown
  448.         if onMouseUp: self.onMouseUp = onMouseUp
  449.         if onKeyDown: self.onKeyDown = onKeyDown
  450.         if onKeyUp: self.onKeyUp = onKeyUp
  451.  
  452.     def child_canvas_socket(self, ctx):
  453.         return CanvasSocket()
  454.  
  455.     width = 1000
  456.     height = 500
  457.  
  458.     onload = None
  459.     onMouseDown = None
  460.     onMouseUp = None
  461.     onMouseMove = None
  462.     onKeyUp = None
  463.     onKeyDown = None
  464.  
  465.     def render_canvas(self, ctx, data):
  466.         return canvas(
  467.             self.width, self.height, self)
  468.  
  469.     docFactory = loaders.stan(tags.html[render_canvas])
  470.  
  471. setattr(Canvas, 'child_nevow_canvas_movie.swf', static.File(
  472.     util.resource_filename('nevow', 'Canvas.swf'),
  473.     'application/x-shockwave-flash'))
  474.  
  475.